--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Commit 6863813a11d7ac61880f6c62e5418bec5e88fd82
Parents : c6ff3be
Author : Ivan <ivan@quad4.io>
Signature : Invalid signer <e46112d44649266d71fe2193e00a4710>, author is <ivan@quad4.io>
Date : 2026-07-08T10:48:18-05:00
fix(PingPage): ensure proper session management during ping operations to terminate previous loops correctly
Changes
2 files changed, 46 insertions(+), 2 deletions(-)
Diff
diff --git a/meshchatx/src/frontend/components/ping/PingPage.vue b/meshchatx/src/frontend/components/ping/PingPage.vue
index febe35fb..ec94b8fc 100644
--- a/meshchatx/src/frontend/components/ping/PingPage.vue
+++ b/meshchatx/src/frontend/components/ping/PingPage.vue
@@ -169,6 +169,7 @@ export default {
pingResults: [],
abortController: null,
lastPingSummary: null,
+ currentSessionId: 0,
};
},
beforeUnmount() {
@@ -204,15 +205,19 @@ export default {
// we are now running ping
this.seq = 0;
this.isRunning = true;
+ this.currentSessionId++;
+ const sessionId = this.currentSessionId;
this.abortController = new AbortController();
// run ping until stopped
- while (this.isRunning) {
+ while (this.isRunning && this.currentSessionId === sessionId) {
// run ping
await this.ping();
// wait a bit before running next ping
- await this.sleep(1000);
+ if (this.isRunning && this.currentSessionId === sessionId) {
+ await this.sleep(1000);
+ }
}
},
async stop() {
diff --git a/tests/frontend/PingPage.test.js b/tests/frontend/PingPage.test.js
index 96563dbc..a2ae7c8e 100644
--- a/tests/frontend/PingPage.test.js
+++ b/tests/frontend/PingPage.test.js
@@ -88,6 +88,45 @@ describe("PingPage.vue", () => {
expect(wrapper.text()).toContain("seq #1");
});
+ it("terminates previous loop when stop and start are called sequentially", async () => {
+ axiosMock.get.mockResolvedValue({
+ data: {
+ ping_result: {
+ rtt: 0.1,
+ hops_there: 1,
+ hops_back: 1,
+ receiving_interface: "UDP",
+ },
+ },
+ });
+
+ const wrapper = mountPingPage();
+ await wrapper.setData({ destinationHash: "a".repeat(32) });
+
+ // track calls
+ const pingSpy = vi.spyOn(wrapper.vm, "ping");
+
+ wrapper.vm.sleep = vi.fn().mockImplementation(() => new Promise((r) => setTimeout(r, 50)));
+
+ // Start 1st loop
+ wrapper.vm.start();
+ await vi.waitFor(() => expect(pingSpy).toHaveBeenCalledTimes(1));
+
+ // Stop 1st loop and start 2nd loop immediately
+ wrapper.vm.stop();
+ wrapper.vm.start();
+
+ // Wait a bit to let any concurrent loop run if it existed
+ await new Promise((r) => setTimeout(r, 120));
+
+ // Total runs should be 1 from the first loop (before stop) and then continuing on the second loop.
+ // If the first loop did not terminate, they would both be calling ping,
+ // resulting in more calls than expected.
+ expect(pingSpy.mock.calls.length).toBeLessThanOrEqual(4);
+
+ wrapper.vm.stop();
+ });
+
it("calls drop path API", async () => {
axiosMock.post.mockResolvedValue({ data: { message: "Path dropped" } });
const wrapper = mountPingPage();
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────